package lib.modules;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import net.fortuna.ical4j.data.CalendarBuilder;
import net.fortuna.ical4j.model.Component;
import net.fortuna.ical4j.model.ComponentList;
import net.fortuna.ical4j.model.Period;
import net.fortuna.ical4j.model.component.VEvent;

import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.calendar.CalendarEntry;

import flexjson.JSONSerializer;

import lib.base.EgoAuth;
import lib.base.EgoException;
import lib.base.EgoHolidayManager;
import lib.base.EgoModule;
import lib.base.EgoSystem;
import lib.data.Department;
import lib.data.Employee;
import lib.json.JSONUnserializer;

public class ModAdmin extends EgoModule
{
	public ModAdmin(HttpServletRequest req) throws Exception
	{
		super(req);
		if (!((EgoAuth) req.getSession().getAttribute("auth")).isAdmin())
		{
			throw new EgoException(EgoException.NOT_AUTHORIZED);
		}
		_request.getSession().setAttribute("template", "modules/admin/index.jsp");
	}
	
	public void work() throws Exception
	{
		try {
			_request.setAttribute(
				"current_employee_account"
				, _curEmp.getField("account").toString()
			);
		} catch (Exception e) {
			// ignore
		}
		String action = _request.getParameter("action");
		if (action != null)
		{
			if (action.equals("read"))
			{
				_read(_request.getParameter("tbl").toString());
			} else if (action.equals("write"))
			{
				_write(_request.getParameter("tbl").toString());
			} else if (action.equals("check_ics"))
			{
				_checkICS(_request.getParameter("url").toString());
			}
		}
	}
	
	private void _read(String config) throws Exception
	{
		Map<String,Object> data = new HashMap<String,Object>();
		
		if (config.equals("customer"))
		{
			data.put("identifier", "customerID");
			data.put("label", "customerID");
			
			ArrayList<Map<String,Object>> items = new ArrayList<Map<String,Object>>();
			items.add(_curCust.getFieldMap());
			
			data.put("items", items);
		} else if (config.equals("department_employee"))
		{
			data.put("identifier", "id");
			data.put("label", "label");
			
			ArrayList<Map<String,Object>> items = new ArrayList<Map<String,Object>>();
			ArrayList<Department> deps = _curCust.getAllDepartments();
			for (int i=0; i<deps.size(); i++)
			{
				Map<String, Object> item_data = new HashMap<String, Object>(deps.get(i).getFieldMap());
				ArrayList<Employee> emps = deps.get(i).getEmployees();
				ArrayList<Map<String,Object>> children = new ArrayList<Map<String,Object>>();
				for (int ii=0; ii<emps.size(); ii++)
				{
					Map<String,Object> attr;
					String id = emps.get(ii).getField("employeeID").toString();
					
					attr = new HashMap<String, Object>();
					attr.put("id", "rel_emp_"+id+"_n"+i+"."+ii); // unique id
					attr.put("label", emps.get(ii).getField("account"));
					// assign role
					attr.put("role", emps.get(ii).getRole(deps.get(i)));
					
					children.add(attr);
				}
				item_data.put("children", children);
				item_data.put("id", "dep_"+item_data.get("departmentID"));
				item_data.put("label", item_data.get("name"));
				items.add(item_data);
			}
			// new department dummy entry
			Map<String, Object> attr_d = new HashMap<String, Object>();
			attr_d.put("id", "dep_X");
			attr_d.put("label", EgoSystem.translator.get(_request.getSession().getAttribute("language").toString()).translate("New Department"));
			attr_d.put("holidayCal", "");
			attr_d.put("workingDays", "31");
			items.add(attr_d);

			/* All employees */
			ArrayList<Employee> allEmps = _curCust.getAllEmployees();
			for (int i=0; i<allEmps.size(); i++)
			{
				Map<String, Object> attr = new HashMap<String, Object>(allEmps.get(i).getFieldMap());
				attr.put("id", "emp_"+attr.get("employeeID"));
				attr.put("label", attr.get("account"));
				attr.put("workingDays", allEmps.get(i).getField("workingDays"));
				if (allEmps.get(i).getOriginalField("workingDays").toString().equals( "0" ))
				{
					attr.put("workingDays_inherit", "1");
				} else
				{
					attr.put("workingDays_inherit", "0");
				}
				attr.put("holidayCal", allEmps.get(i).getOriginalField("holidayCal"));
				ArrayList<Department> emp_deps = allEmps.get(i).getDepartments();
				String deps_str = ",";
				for (int ii=0; ii<emp_deps.size(); ii++)
				{
					deps_str += "dep_" + emp_deps.get(ii).getField("departmentID") + ",";
				}
				attr.put("departments", deps_str);
				items.add(attr);
			}
			// new employee dummy entry
			Map<String, Object> attr = new HashMap<String, Object>();
			attr.put("id", "emp_X");
			attr.put("label", EgoSystem.translator.get(_request.getSession().getAttribute("language").toString()).translate("New Employee"));
			attr.put("givenName", "");
			attr.put("lastName", "");
			attr.put("entitlementLeave", "0");
			attr.put("specialLeave", "0");
			attr.put("remainingLeavePrevYear", "0");
			attr.put("manageOwnLeaves", "0");
			attr.put("isAdmin", "0");
			attr.put("workingDays_inherit", "1");
			items.add(attr);
			
			data.put("items", items);
		}
		
		_request.setAttribute("ajax_return", new JSONSerializer().serialize(data));
		_request.getSession().setAttribute("template", "ajax_return.jsp");
	}
	
	@SuppressWarnings("unchecked")
	private void _write(String config) throws Exception
	{
		String json = _request.getParameter("data").toString();
		
		Object json_data = new JSONUnserializer().unserialize(json);
		
		if (config.equals("customer"))
		{
			ArrayList<HashMap<String, Object>> items = (ArrayList<HashMap<String, Object>>) ((HashMap<String, Object>) json_data).get("items");
			for (int i=0; i<items.size(); i++)
			{
				String customer_id = items.get(i).get("customerID").toString();
				if (!customer_id.equals(_curCust.getField("customerID").toString()))
				{
					throw new Exception("Security. The customerID to write is not the current customer!");
				}
				_curCust.update(items.get(i));
			}
		} else if (config.equals("department_employee"))
		{
			// update all employees and departments
			// TODO how to typecast safety?
			ArrayList<HashMap<String, Object>> items = (ArrayList<HashMap<String, Object>>) ((HashMap<String, Object>) json_data).get("items");
			
			Map<Department, Map<String, String>> relations = new HashMap<Department, Map<String, String>>();
			ArrayList<Department> departments_to_delete = _curCust.getAllDepartments();
			ArrayList<Employee> employees_to_delete = _curCust.getAllEmployees();
			for (int i=0; i<items.size(); i++)
			{
				HashMap<String, Object> new_data = items.get(i);
				String id = new_data.get("id").toString();
				if (id.matches("^dep_.*"))
				{
					if (id.equals( "dep_X" )) continue;

					/*
					 * update department
					 */
					String dep_id = id.substring(4);
					for (int ii=0; ii<departments_to_delete.size(); ii++)
					{
						if (departments_to_delete.get(ii).getField("departmentID").toString().equals(dep_id))
						{
							// this department exists and must not be deleted
							departments_to_delete.remove(ii);
						}
					}
					Department dep;
					if (dep_id.charAt(0)=='n')
					{ // create new
						dep = Department.newDepartment(_curCust);
					} else
					{ // change existing
						dep = _curCust.getDepartment(dep_id);
						// rename department's calendar
						if (!dep.getField("name").toString().equals( new_data.get("label").toString() ))
						{
							String title = EgoHolidayManager.HOLIDAY_DEPARTMENT_CALENDAR_TITLE_FORMAT
								.replaceAll("%name%", new_data.get("label").toString());
							CalendarEntry cal = _curCust.holidayManager.getCalendar(dep);
							cal.setTitle(new PlainTextConstruct(title));
							cal.update();
						}
					}
					// label is name
					new_data.put("name", new_data.get("label"));
					new_data.put("customerID", null); // customerID cannot be changed
					dep.update(new_data);
					
					/*
					 * update department_employee relation
					 */
					ArrayList<Object> children = null;
					if (new_data.get("children") instanceof HashMap<?,?>)
					{
						children = new ArrayList<Object>();
						children.add(new_data.get("children"));
					} else if (new_data.get("children") instanceof ArrayList<?>)
					{
						// first only save the reference, without role
						children = (ArrayList<Object>) new_data.get("children");
					}

					Map<String, String> dep_children = new HashMap<String, String>();
					if (children != null) for (int ii=0; ii<children.size(); ii++)
					{
						HashMap<String, Object> attr = (HashMap<String, Object>)children.get(ii);
						dep_children.put(attr.get("_reference").toString(), "");
					}
					relations.put(dep, dep_children);
				} else if (id.matches("^emp_.*"))
				{
					if (id.equals( "emp_X" )) continue;
					
					/*
					 * update employee
					 */
					String emp_id = id.substring(4);
					for (int ii=0; ii<employees_to_delete.size(); ii++)
					{
						if (employees_to_delete.get(ii).getField("employeeID").toString().equals(emp_id))
						{
							// this employee exists and must not be deleted
							employees_to_delete.remove(ii);
						}
					}
					Employee emp;
					if (emp_id.charAt(0)=='n')
					{ // create new
						emp = Employee.newEmployee(_curCust);
					} else
					{ // change existing
						emp = _curCust.getEmployee(emp_id);
						
						// check if name has changed
						if (
							!emp.getField("givenName").toString().equals( new_data.get("givenName").toString() )
							|| !emp.getField("lastName").toString().equals( new_data.get("lastName").toString() )
						) {
							// rename employee's calendar 
							String title = EgoHolidayManager.HOLIDAY_CALENDAR_TITLE_FORMAT
								.replaceAll("%given name%", new_data.get("givenName").toString())
								.replaceAll("%last name%", new_data.get("lastName").toString());
							CalendarEntry cal = _curCust.holidayManager.getCalendar(emp);
							if (cal != null)
							{
								cal.setTitle(new PlainTextConstruct(title));
								cal.update();
							}
						}
					}
					// label is account
					new_data.put("account", new_data.get("label"));
					new_data.put("customerID", null); // customerID cannot be changed
					new_data.put("role", null); // role cannot be changed here
					if (new_data.get("workingDays_inherit") == null || new_data.get("workingDays_inherit").toString().equals( "1" ))
					{
						new_data.put("workingDays", "0");
					}
					new_data.put("workingDays_inherit", null);
					emp.update(new_data);
				} else if (id.matches("^rel_emp_.*"))
				{
					for (Map.Entry<Department, Map<String, String>> entry : relations.entrySet())
					{// all departments
						Map<String,String> list = entry.getValue();
						if (list.get(id) != null)
						{// is there a emp reference
							list.put(
								new_data.get("label").toString(),
								new_data.get("role").toString()
							);
							list.remove(id);
						}
					}
				}
			}
			
			// next update relations
			ArrayList<Employee> emps = _curCust.getAllEmployees();
			for (Map.Entry<Department, Map<String, String>> entry : relations.entrySet())
			{// iterates departments
				Map<String, String> children = entry.getValue();
				ArrayList<Employee> new_children = new ArrayList<Employee>();
				next_child:
				for (Map.Entry<String, String> entry2 : children.entrySet())
				{// iterates employees => roles
					// find the employee
					for (int ii=0; ii<emps.size(); ii++)
					{
						if (entry2.getKey().equals(emps.get(ii).getField("account").toString()))
						{
							emps.get(ii).setField("role", entry2.getValue());
							new_children.add(emps.get(ii));
							continue next_child;
						}
					}
					//not found
					throw new Exception("error while updating relations: no employee found for:"+entry2.getKey());
				}
				entry.getKey().updateEmployeesRelations(new_children);
			}
			
			// delete employees and departments from DB
			for (int i=0; i<departments_to_delete.size(); i++)
			{
				departments_to_delete.get(i).delete();
			}
			for (int i=0; i<employees_to_delete.size(); i++)
			{
				employees_to_delete.get(i).delete();
			}
		}
		
		_curCust.refresh();
		
		// at least setup the google calendar
		ArrayList<Employee> emps = _curCust.getAllEmployees();
		for (int i=0; i<emps.size(); i++)
		{
			_curCust.holidayManager.setupCalendar(emps.get(i));
		}
		ArrayList<Department> deps = _curCust.getAllDepartments();
		for (int i=0; i<deps.size(); i++)
		{
			_curCust.holidayManager.setupCalendar(deps.get(i));
		}
		_request.getSession().setAttribute("template", "ajax_return.jsp");
	}
	
	private void _checkICS(String url)
	{
		String result;
		
		Calendar cal = Calendar.getInstance(); /* create the current Calendar of this year */
		String year = String.valueOf(cal.get(Calendar.YEAR));
		
		try
		{
			net.fortuna.ical4j.model.Date rangeStart = new net.fortuna.ical4j.model.Date(year+"0101");
			net.fortuna.ical4j.model.Date rangeEnd = new net.fortuna.ical4j.model.Date(String.valueOf(Integer.parseInt(year)+1)+"0101");
			
			net.fortuna.ical4j.model.DateTime rangeStart_t = new net.fortuna.ical4j.model.DateTime(rangeStart);
			net.fortuna.ical4j.model.DateTime rangeEnd_t = new net.fortuna.ical4j.model.DateTime(rangeEnd);
			
			CalendarBuilder builder = new CalendarBuilder();
			net.fortuna.ical4j.model.Calendar calendar = builder.build(
				new StringReader( EgoSystem.httpRequest(url) )
			);
			ComponentList comps = calendar.getComponents(Component.VEVENT);
			for (int i=0; i<comps.size(); i++)
			{
				VEvent x = (VEvent)(comps.get(i));
				x.calculateRecurrenceSet(new Period(rangeStart_t, rangeEnd_t));
			}
			
			result = "true";
		} catch (Exception e)
		{
			result = "false";
		}

		_request.setAttribute("ajax_return", result);
		_request.getSession().setAttribute("template", "ajax_return.jsp");
	}
}
